home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 220 / 220.xpi / chrome / flashgot.jar / content / flashgot / flashgotGalleryBuilder.js < prev    next >
Encoding:
JavaScript  |  2010-01-24  |  20.0 KB  |  681 lines

  1. /***** BEGIN LICENSE BLOCK *****
  2.  
  3.     FlashGot - a Firefox extension for external download managers integration
  4.     Copyright (C) 2004-2009 Giorgio Maone - g.maone@informaction.com
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.                              
  20. ***** END LICENSE BLOCK *****/
  21.  
  22. function FlashGotGalleryBuilder() {}
  23.  
  24. FlashGotGalleryBuilder.INTERVAL_RX=/\[\s*(\d+)\s*-\s*(\d+)\s*(;{0,1}\s*\d*)\s*\]/;
  25. FlashGotGalleryBuilder.INTERVAL_AZ_RX=/\[\s*([a-z]{1})\s*-\s*([a-z]{1})\s*(;{0,1}\s*\d*)\s*\]/i;
  26. FlashGotGalleryBuilder.EXPR_RX=/\[\s*([\w]+)\s*\((.*?)\)\s*\]/i;
  27.  
  28. FlashGotGalleryBuilder.prototype = {
  29.   
  30.   urlsTableModel: {
  31.     selection: null,
  32.     data: [],
  33.     get rowCount() { return this.data.length; },
  34.     getCellText: function(row, col) {
  35.       return this.data[row][col.id?col.id:col];
  36.     },
  37.     setTree: function(treeBox) { this.treeBox = treeBox; },
  38.     isContainer: function(index) { return false; },
  39.     isSeparator: function(index) { return false; }, 
  40.     isSorted: function() { return false; },
  41.     getLevel: function(index) { return 0; },
  42.     getImageSrc: function(row, col) {
  43.       return null;
  44.     },
  45.     getCellProperties: function(row, col, props) {},
  46.     getColumnProperties: function(column, elem, prop) {}, 
  47.     getRowProperties: function(row, props) { },
  48.   
  49.     isContainerOpen: function(index) { },
  50.     isContainerEmpty: function(index) { return false; },
  51.     canDropOn: function(index) { return false; },
  52.     canDropBeforeAfter: function(index, before) { return false; },
  53.     drop: function(row, orientation) { return false; },
  54.     
  55.     getParentIndex: function(index) { return 0; },
  56.     hasNextSibling: function(index, after) { return false; },
  57.     getProgressMode: function(row, column) { },
  58.     getCellValue: function(row, column) { },
  59.     toggleOpenState: function(index) { },
  60.     cycleHeader: function(col, elem) { },
  61.     selectionChanged: function() {
  62.       try {
  63.         gFlashGotGB.urlsPreviewDoc.getElementById(
  64.             FlashGotGalleryHTML.prototype.galleryId).innerHTML =
  65.             this.data[this.selection.currentIndex].html;
  66.       } catch(ex) {
  67.       } 
  68.     },
  69.     cycleCell: function(row, column) { },
  70.     isEditable: function(row, column) { return false; },
  71.     performAction: function(action) { },
  72.     performActionOnRow: function(action, row) { },
  73.     performActionOnCell: function(action, row, column) { }
  74.   },
  75.   
  76.   sandbox: Components.utils.Sandbox ? Components.utils.Sandbox("about:") : null,
  77.   expressions: {},
  78.   selectedExprName: null,
  79.   onload: function() {
  80.     try {
  81.       var data=window.arguments[0];
  82.       this.previewTextBox.value=data.previewURL;
  83.       this.contentTextBox.value=data.contentURL;
  84.       this.referrerTextBox.value=data.referrerURL;
  85.       this.originalWindow=data.originalWindow;
  86.       this.tmpDir=data.tmpDir;
  87.       this.prefs=data.prefs;
  88.       this.filePath=null;
  89.       if(this.sandbox) {
  90.         try {
  91.           this.expressions = Components.utils.evalInSandbox(this.prefs.getCharPref("buildGallery.expressions"), this.sandbox);
  92.         } catch(ex) {}
  93.         if(typeof(this.expressions)!="object" || !this.expressions) this.expressions = {};
  94.       } else {
  95.         document.getElementById("flashgotGB-expr-tab").setAttribute("disabled", "true");
  96.       }
  97.       this.normalizeURL(this.previewTextBox);
  98.       this.normalizeURL(this.contentTextBox);
  99.       
  100.       function fixColLabel(id) {
  101.         var col = document.getElementById(id + "Col");
  102.         col.setAttribute("label",col.getAttribute("label").replace(/:/g,""));
  103.       }
  104.       fixColLabel("preview");
  105.       fixColLabel("content");
  106.       this.urlsTable.view = this.urlsTableModel;
  107.       this.validateURLs();
  108.       document.getElementById("mainTabs").setAttribute("onselect","gFlashGotGB.tabSelected(event)");
  109.     } catch(e) {
  110.       gFlashGotService.log(e);
  111.       window.dump(e);
  112.       this.dialog.cancelDialog();
  113.     }
  114.     
  115.   }
  116. ,
  117.   saveExpressions: function() {
  118.     if(typeof(this.expressions)=="object" && this.expressions) {
  119.       this.prefs.setCharPref("buildGallery.expressions",this.expressions.toSource());
  120.     }
  121.   }
  122. ,
  123.   get dialog() {
  124.     return document.documentElement;
  125.   }
  126. ,
  127.   get previewBase() {
  128.     return this.trim(this.previewTextBox.value);
  129.   }
  130. , get contentBase() {
  131.     return this.trim(this.contentTextBox.value);
  132.   }
  133. , get referrer() {
  134.     return this.trim(this.referrerTextBox.value);
  135.   }
  136. ,
  137.   get previewTextBox() {
  138.     return document.getElementById("flashgotGB-preview-text");
  139.   }
  140. ,  
  141.   get contentTextBox() {
  142.     return document.getElementById("flashgotGB-content-text");
  143.   }
  144. ,
  145.   get referrerTextBox() {
  146.     return document.getElementById("flashgotGB-referrer-text");
  147.   }
  148.   get urlsTable() {
  149.     return document.getElementById("flashgotGB-urlsTable");
  150.   }
  151. ,
  152.   get urlsPreviewDoc() {
  153.     return document.getElementById("flashgotGB-urls-preview").contentDocument;
  154.   }
  155. ,
  156.   get exprListBox() { 
  157.     return document.getElementById("flashgotGB-expr-list");
  158.   }
  159. ,
  160.   get exprTextBox() {
  161.     return document.getElementById("flashgotGB-expr-text");
  162.   }
  163. ,
  164.   trim: function(s) {
  165.     return s.replace(/^\s+/g,"").replace(/\s+$/g,"");
  166.   }
  167.   checkIntervals: function(url) {
  168.     return url.search(FlashGotGalleryBuilder.INTERVAL_RX)>-1 || url.search(FlashGotGalleryBuilder.INTERVAL_AZ_RX)>-1;
  169.   }
  170. ,
  171.   normalizeURL: function(textBox) { 
  172.     var url=textBox.value;
  173.     var hasIntervals=this.checkIntervals(url);
  174.     if(!hasIntervals) {
  175.       url=url.replace(/(\{|\(|<)/g,"[").replace(/(\}|\)|>)/g,"]");
  176.       textBox.value=this.checkIntervals(url)
  177.         ?url
  178.         :textBox.value.replace(/(\d+)/g,"[$1-$1;1]");
  179.       }
  180.   }
  181. ,  
  182.   validateURLs: function() {
  183.  
  184.     var htmlBuilder = new FlashGotGalleryHTML(this);
  185.    
  186.     var valid = htmlBuilder.valid;
  187.     this.dialog.getButton("accept").setAttribute("disabled", !valid);
  188.     const model= this.urlsTableModel;
  189.     model.treeBox.rowCountChanged(0, -model.data.length);
  190.     const urlList = model.data = [];
  191.  
  192.     if(valid) {
  193.       for(var html; html = htmlBuilder.nextFragment();) {
  194.         urlList[urlList.length] = {
  195.           html: html,
  196.           previewCol: htmlBuilder.currentPreviewURL,
  197.           contentCol: htmlBuilder.currentContentURL
  198.         }
  199.       }
  200.       model.treeBox.rowCountChanged(0, urlList.length);
  201.     }
  202.     
  203.     
  204.     const exprListBox=this.exprListBox;
  205.     const selectedExprName=this.selectedExprName;
  206.     exprListBox.setAttribute("suppressonselect","true");
  207.     while(exprListBox.getRowCount()>0) exprListBox.removeItemAt(0);
  208.     const exprNames=htmlBuilder.exprNames;
  209.     var selectedItem=null;
  210.     var len=exprNames.length;
  211.     for(var j=0; j<len; j++) {
  212.       item=exprListBox.appendItem(exprNames[j]);
  213.       if(selectedItem==null || item.label==selectedExprName) selectedItem=item;
  214.     }
  215.     
  216.     if(selectedItem) {
  217.       exprListBox.selectItem(selectedItem);
  218.     }
  219.     this.exprSelected();
  220.     
  221.     exprListBox.removeAttribute("suppressonselect");
  222.  
  223.   }
  224. ,
  225.   synchronizePreview: function() {
  226.     this.synchronizeIntervals(this.contentTextBox,this.previewTextBox,
  227.     { rx: /\.(mpg|mp4|gvi|flv|swf|mpeg|wmv|avi|mov|divx|ogm)$/i, ext: ".jpg" });
  228.   }
  229. ,
  230.   synchronizeContent: function() {
  231.     this.synchronizeIntervals(this.previewTextBox,this.contentTextBox,
  232.     { rx: /\.(jpg|jpeg|gif|png|bmp)$/i, ext: ".mpg" });
  233.   }
  234. ,
  235.   synchronizeIntervals: function(srcBox,dstBox,extFix) {
  236.     var dst=this.trim(dstBox.value);
  237.     if(dst=="") {
  238.       dst=srcBox.value.replace(extFix.rx,extFix.ext);
  239.     } else {
  240.       var isrc=new FlashGotGalleryIterator(this.trim(srcBox.value));
  241.       var idst=new FlashGotGalleryIterator(dst);
  242.       dst="";
  243.       var src=""
  244.       while(isrc && idst && isrc.valid && idst.valid) {
  245.         dst=dst.concat(
  246.             idst.base.substring(0,idst.match.index)
  247.           ).concat(
  248.             isrc.match[0]
  249.           );
  250.          isrc=isrc.delegate;
  251.          idst=idst.delegate;
  252.       }
  253.     }
  254.     if(idst) dst=dst.concat(idst.base);
  255.     dstBox.value=dst;
  256.     this.validateURLs();
  257.   }
  258. ,
  259.   build: function() {
  260.     var htmlBuilder=new FlashGotGalleryHTML(this);
  261.   
  262.     const cc=Components.classes;
  263.     const ci=Components.interfaces;
  264.    
  265.     const galFile=cc["@mozilla.org/file/local;1"].createInstance(ci.nsILocalFile);
  266.     galFile.initWithPath(this.tmpDir.path);
  267.     galFile.append("flashgotGB.html");
  268.     galFile.createUnique(0,-1);
  269.     
  270.     this.filePath=galFile.path;
  271.     
  272.     const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  273.       ci.nsIFileOutputStream);
  274.     
  275.     try {
  276.       os.init(galFile,0x02,-1,0);
  277.       
  278.       var html = htmlBuilder.header; 
  279.       os.write(html, html.length);
  280.       
  281.       while( (html = htmlBuilder.nextFragment()) ) {
  282.         os.write(html, html.length);
  283.         if(!htmlBuilder.valid) break;
  284.       }
  285.       
  286.       html = htmlBuilder.footer;
  287.       os.write(html,html.length);
  288.     
  289.     } finally {
  290.       os.close();
  291.     }
  292.     
  293.     var w = this.originalWindow;
  294.     var url = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService).newFileURI(galFile).spec;
  295.     if(typeof(w.messenger)=="object" && w.messenger.OpenURL) { 
  296.       // Thunderbird
  297.       w.messenger.OpenURL(url);
  298.     } else if(w.closed) {
  299.       w = window.open(url, "_blank");
  300.     } else {
  301.       var browser = w.getBrowser();
  302.       browser.selectedTab = browser.addTab(url);
  303.     }
  304.   }
  305. ,
  306.   createExpression: function(text) {
  307.     expr = { text: text };
  308.     try {
  309.       expr.func = Components.utils.evalInSandbox("_f = function() {\n" + text + "\n}", this.sandbox);
  310.       expr.err = null;
  311.     } catch(err) {
  312.       expr.err = err;
  313.     }
  314.     return expr;
  315.   }
  316. ,
  317.   func2Expr: function(func) {
  318.     var f = func.toString();
  319.     f=f.substring(f.indexOf('{')+1,f.lastIndexOf('}'));
  320.     var indent=f.match(/^([ \t]+)\w/m);
  321.     if(indent) {
  322.       var spaces=indent[1];
  323.       var rxSpaces=new RegExp(spaces,'g');
  324.       f=f.replace(new RegExp('^'+spaces+'((?:'+spaces+')*)','mg'),
  325.       function($0,$1) {
  326.         return $1.replace(rxSpaces,' ');
  327.       });
  328.     }
  329.     return f.replace(/^[\s]*\n/gm,'');
  330.   }
  331. ,
  332.   tabSelected: function(ev) {
  333.     switch(ev.target.selectedItem.id) {
  334.       case "flashgotGB-url-tab":
  335.         this.exprChanged();
  336.         this.validateURLs();
  337.         break;
  338.       case "flashgotGB-expr-tab":
  339.           this.validateURLs();
  340.           this.exprTextBox.focus();
  341.     }
  342.   }
  343. ,
  344.   exprSelected: function() {
  345.     const exprTextBox=this.exprTextBox;
  346.     const exprDes=document.getElementById("flashgotGB-expr-des");
  347.     const rxFxName=/\bfunction \w+\(/;
  348.     const errorTextBox=document.getElementById("flashgotGB-expr-error-text");
  349.     const selectedItem=this.exprListBox.selectedItem;
  350.     this.exprChanged();
  351.     if(!selectedItem) {
  352.       this.selectedExprName=null;
  353.       exprTextBox.value="";
  354.       exprDes.value=exprDes.value.replace(rxFxName,"function fx(");
  355.       exprTextBox.setAttribute("disabled",true);
  356.       errorTextBox.value="";
  357.     } else {
  358.       exprTextBox.removeAttribute("disabled");
  359.       const exprName=selectedItem.label;
  360.       this.selectedExprName=exprName;
  361.       const expr=this.expressions[exprName];
  362.       exprTextBox.value=expr?expr.text:'return "";';
  363.       exprDes.value=exprDes.value.replace(rxFxName,"function "+exprName+"(");
  364.       this.exprChanged();
  365.     }
  366.   }
  367.   exprChanged: function() {
  368.     const selectedExprName=this.selectedExprName;
  369.     if(!selectedExprName) {
  370.       this.exprTextBox.setAttribute("disabled",true);
  371.     } else {
  372.       this.exprTextBox.removeAttribute("disabled");
  373.       var text=this.exprTextBox.value;
  374.       var expr=this.expressions[selectedExprName];
  375.       if(text.replace(/\s+/,'').length) {
  376.         if( (!expr) || expr.text!=text) {
  377.           expr = this.expressions[selectedExprName] = this.createExpression(text);
  378.           this.saveExpressions();
  379.         }
  380.       } else if(expr) {
  381.         delete this.expressions[selectedExprName];
  382.         this.saveExpressions();
  383.       }
  384.     }
  385.     this._hilightErrors();
  386.   }
  387. ,
  388.   _hilightErrors: function() {
  389.     const exprList=this.exprListBox;
  390.     exprList.style.background="white";
  391.     const ee=this.expressions;
  392.     var item,expr;
  393.     for(var j=exprList.getRowCount(); j-->0;) {
  394.       item=exprList.getItemAtIndex(j);
  395.       expr=ee[item.label];
  396.       if(expr) {
  397.         item.style.color=expr.err?"red":"black";
  398.         if(item.selected) {
  399.           document.getElementById("flashgotGB-expr-error-text").value=expr.err;
  400.           this.exprTextBox.style.color=item.style.color;
  401.         }
  402.       }
  403.     }
  404.   }
  405. }
  406.  
  407.  
  408. function FlashGotGalleryHTML(builder) {
  409.   this.builder=builder;
  410.   this.previews=new FlashGotGalleryIterator(builder.previewBase);
  411.   this.contents=new FlashGotGalleryIterator(builder.contentBase);
  412.   var exprNames=[];
  413.   var name,expr;
  414.   
  415.   for(var base=builder.previewBase.concat(builder.contentBase), match=null;
  416.     match=base.match(FlashGotGalleryBuilder.EXPR_RX);
  417.     base=base.substring(match.index+match[0].length)
  418.     ) {
  419.      exprNames[exprNames.length]=match[1];
  420.   }
  421.   const ee=builder.expressions;
  422.   
  423.   for(name in ee) {
  424.     exprNames[exprNames.length]=name;
  425.     expr=ee[name];
  426.     this.env[name] = expr.func ? expr.func : function() { throw new Error("["+name+"()]"+" not implemented!"); };
  427.   }
  428.   
  429.   exprNames=exprNames.sort();
  430.   for(var prevName=null, j=exprNames.length; j-->0;) {
  431.     name=exprNames[j];
  432.     if(name==prevName) exprNames.splice(j,1);
  433.     else prevName=name;
  434.   }
  435.   this.exprNames=exprNames;
  436.   this.buildDOM(this.builder.urlsPreviewDoc);
  437. }
  438.  
  439. FlashGotGalleryHTML.prototype = {
  440.   index: 0,
  441.   env: Components.utils.Sandbox ? Components.utils.Sandbox("about:blank") : null,
  442.   galleryId: "flashgotGB-gallery",
  443.   xmlesc: function(s) {
  444.     return s && s.replace(/[&"<>]/g, 
  445.       function(c) { return { '"': '"', '<': '<', '>': '>', '&': '&' }[c] }
  446.     ) || "";
  447.   }
  448. ,
  449.   get headElementSource() {
  450.  
  451.     return '<head><title>' 
  452.         + this.xmlesc(this.builder.referrer + " - "
  453.             + this.builder.dialog.getAttribute('title')) 
  454.         + '</title>\n'
  455.         +'<style type="text/css">\n'
  456.         +'body,div { font-family: verdana,arial,helvetica,sans-serif; '
  457.         +'font-size: 10px; color: black; background: white }\n'
  458.         +'a { color: blue; text-decoration: underline; }\n'
  459.         +'</style></head>'
  460.         ;
  461.   }
  462.   get header() {
  463.     const persist = {
  464.       referrer: this.builder.referrer,
  465.       preview: this.previews.base,
  466.       content: this.contents.base
  467.     };
  468.     var h = this.headElementSource + '<body><div style="display: none">';
  469.     for(var p in persist) {
  470.       h += '<span id="' + p + '">' + this.xmlesc(persist[p]) + '</span>';
  471.     }
  472.     return h + '</div><div id="' + this.galleryId + '">';
  473.   }
  474. ,  
  475.   get footer() {
  476.     return "\n</div></body>";
  477.   }
  478. ,
  479.   _eval: function(ctx, parm) {
  480.     return Components.utils.evalInSandbox("_func(" + parm + ")", ctx);
  481.   }
  482. ,
  483.   // evaluates macros and javascript functions
  484.   _macroPattern: /\[\$(\d+)\]/g,
  485.   evalExpressions: function(iterator) {
  486.     var url = iterator.nextURL();
  487.     if (!url) return url;
  488.     const index = this.index;
  489.     
  490.     // macro evaluation
  491.     url = url.replace(this._macroPattern, function(all, digits) {
  492.       var n = parseInt(digits);
  493.       const len = digits.length;
  494.       var res;
  495.       if (n == 0) {
  496.          res = index.toString();
  497.       } else {
  498.         var delegate = iterator;
  499.         while (n-- > 1 && delegate) delegate = delegate.delegate;
  500.         if (!delegate) return all;
  501.         res = delegate.renderedCursor;
  502.         if (len > 0) res = res.toString().replace(/^0+/, "");
  503.       }
  504.       
  505.       return len > 0
  506.         ? digits.replace(/\d/g, '0').substring(0, len - res.length).concat(res)
  507.         : res;
  508.     });
  509.     
  510.     if(!(this.exprNames.length && this.env))  return url;
  511.     
  512.     // function evaluation
  513.     var base = url;
  514.     var evalURL = "";
  515.     const ee=this.builder.expressions;
  516.     const ctx = this.env;
  517.     ctx.index = index;
  518.     ctx.baseURL = base;
  519.     
  520.     var name, expr, subst, res;
  521.     
  522.     for(var match=null;
  523.         match=base.match(FlashGotGalleryBuilder.EXPR_RX);
  524.         base=base.substring(match.index + match[0].length)
  525.     ) {
  526.       name=match[1];
  527.       expr=ee[name];
  528.       subst=match[0];
  529.       if(expr && expr.func) {
  530.         try {
  531.           ctx._func=expr.func;
  532.           res = this._eval(ctx, match[2]);
  533.           if(res != null && typeof(res) != "undefined") {
  534.             subst=res;
  535.           }
  536.           expr.err=null;
  537.         } catch(err) {
  538.           expr.err=err;
  539.         }
  540.       }
  541.       evalURL+=base.substring(0,match.index).concat(subst);
  542.     }
  543.     evalURL+=base;
  544.     return evalURL;
  545.   }
  546. ,
  547.   nextFragment: function() {
  548.     this.index++;
  549.     var p = this.currentPreviewURL = 
  550.       this.evalExpressions(this.previews);
  551.     var c = this.currentContentURL = 
  552.       this.evalExpressions(this.contents);
  553.     if( 
  554.        (! (p || c) )
  555.       || (p==null && !this.contents.valid) 
  556.       || (c==null && !this.previews.valid) 
  557.     ) {
  558.       return null;
  559.     }
  560.     c = this.xmlesc(c);
  561.     p = this.xmlesc(p);
  562.     var html = p ? '<img src="' + p + '" alt="' + ( c ? c : "???" )+'" />': c + '<br />\n';
  563.     if(c) html = '<a href="' + c + '">' + html + '</a>\n';
  564.     return html;
  565.   }
  566. ,
  567.   get valid() {
  568.     return this.previews.valid || this.contents.valid;
  569.   }
  570. ,  
  571.   reset: function() {
  572.     this.previews.reset();
  573.     this.contents.reset();
  574.     this.index = 0;
  575.   }
  576. ,
  577.   buildDOM: function(doc) {
  578.     if(!doc.getElementById(this.galleryId)) {
  579.       doc.documentElement.innerHTML = this.headElementSource;
  580.       doc.documentElement.appendChild(doc.createElement("body")
  581.       ).appendChild(doc.createElement("div")).id = this.galleryId;
  582.     }
  583.   }
  584.  
  585. }
  586.  
  587.  
  588. function FlashGotGalleryIterator(base) {
  589.   this.base=base;
  590.   var match=FlashGotGalleryBuilder.INTERVAL_RX.exec(this.base);
  591.   var matchAZ=FlashGotGalleryBuilder.INTERVAL_AZ_RX.exec(this.base);
  592.   if(match && ( (!matchAZ) || matchAZ.index>match.index) ) {
  593.     var idx = match.index;
  594.     this.isAZ = false;
  595.     this.start = parseInt(match[1],10);
  596.     this.end = parseInt(match[2],10);
  597.     this.padding = "";
  598.     for(var j=(this.end>this.start?match[1]:match[2]).length; 
  599.       j-->0; 
  600.       this.padding=this.padding.concat("0") 
  601.       );
  602.     this.valid=true;
  603.   } else if((this.isAZ=((match=matchAZ)!=null))) {
  604.     if(/[a-z]{1}/.test(match[1])) {
  605.       match[2] = match[2].toLowerCase();
  606.     } else {
  607.       match[2] = match[2].toUpperCase();
  608.     }
  609.     this.start = match[1].charCodeAt(0);
  610.     this.end = match[2].charCodeAt(0);  
  611.     this.valid = true;
  612.   } else {
  613.     this.valid = false;
  614.     return;
  615.   }
  616.   
  617.   this.match = match;
  618.   
  619.   var stepMatch = this.match[3].match(/;\s*(\d+)/);
  620.   this.step =  (stepMatch ? parseInt(stepMatch[1], 10) : 1) * (this.start <= this.end ? 1 : -1);
  621.     
  622.   this.cursor=this.start;
  623.  
  624.   this.delegate=new FlashGotGalleryIterator(
  625.     this.match.input.substring(this.match.index+this.match[0].length)
  626.     );
  627. }
  628.   
  629. FlashGotGalleryIterator.prototype = {
  630.   renderedCursor: "",
  631.   reset: function() {
  632.     this.cursor=this.start;
  633.     if(this.delegate) this.delegate.reset();
  634.   }
  635. ,
  636.   nextURL: function() {
  637.    
  638.     if(!this.valid) return this.base;
  639.     if(this.step==0
  640.       || (this.step>0 && this.cursor>this.end)
  641.       || (this.step<0 && this.cursor<this.end)) {
  642.       return null;
  643.     }
  644.     
  645.     var count;
  646.     
  647.     if(this.isAZ) {
  648.       count=String.fromCharCode(this.cursor);
  649.     } else {
  650.       count=new String(this.cursor);
  651.       if(count.length<this.padding.length) {
  652.         count=this.padding.substring(count.length).concat(count);
  653.       }
  654.     }
  655.     
  656.     var delegatePart=this.delegate.nextURL();
  657.     if(delegatePart==null || !this.delegate.valid) {
  658.       this.cursor+=this.step;
  659.       if(delegatePart==null) {
  660.         this.delegate.reset();
  661.         return this.nextURL();
  662.       }
  663.     }
  664.     
  665.     this.renderedCursor=count;
  666.     
  667.     return this.match.input.substring(0,this.match.index
  668.       ).concat(count
  669.       ).concat(delegatePart);  
  670.    
  671.   }
  672.   
  673. }
  674.  
  675. var gFlashGotGB=new FlashGotGalleryBuilder();
  676.  
  677.